package com.qiujie.service;

import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.sql.Date;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;

import java.util.stream.Collectors;
import java.util.stream.IntStream;


import javax.servlet.http.HttpServletResponse;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qiujie.dto.Response;
import com.qiujie.dto.ResponseDTO;
import com.qiujie.entity.Attendance;
import com.qiujie.entity.Dept;
import com.qiujie.enums.AttendanceStatusEnum;
import com.qiujie.enums.BusinessStatusEnum;
import com.qiujie.mapper.AttendanceMapper;
import com.qiujie.mapper.DeptMapper;
import com.qiujie.mapper.StaffMapper;
import com.qiujie.util.DatetimeUtil;
import com.qiujie.util.HutoolExcelUtil;
import com.qiujie.vo.StaffAttendanceVO;

import cn.hutool.core.date.DateUtil;


@Service
@Slf4j
public class AttendanceService extends ServiceImpl<AttendanceMapper, Attendance> {

    @Autowired
    private AttendanceMapper attendanceMapper;

    @Autowired
    private DeptMapper deptMapper;

    @Autowired
    private StaffMapper staffMapper;

    @Autowired
    private DatetimeUtil datetimeUtil;

    public ResponseDTO add(Attendance attendance) {
        if (save(attendance)) {
            return Response.success();
        }
        return Response.error();
    }

    public ResponseDTO delete(Integer id) {
        if (removeById(id)) {
            return Response.success();
        }
        return Response.error();
    }

    @Transactional(rollbackFor = Exception.class)
    public ResponseDTO deleteBatch(List<Integer> ids) {
        if (removeBatchByIds(ids)) {
            return Response.success();
        }
        return Response.error();
    }

    public ResponseDTO edit(Attendance attendance) {
        if (updateById(attendance)) {
            return Response.success();
        }
        return Response.error();
    }

    public ResponseDTO query(Integer id) {
        Attendance attendance = getById(id);
        if (attendance != null) {
            return Response.success(attendance);
        }
        return Response.error();
    }


    public ResponseDTO list(Integer current, Integer size, String name, Integer deptId, String month) {
        IPage<StaffAttendanceVO> config = new Page<>(current, size);
        // 解决当搜索条件为空时，默认查询所有数据
        if (name == null) {
            name = "";
        }
        IPage<StaffAttendanceVO> page;
        if (deptId == null) {
            page = this.staffMapper.listStaffAttendanceVO(config, name);
        } else {
            page = this.staffMapper.listStaffDeptAttendanceVO(config, name, deptId);
        }
        // 每页展示的数据
        List<StaffAttendanceVO> staffDeptVOList = page.getRecords();
        // 如果没有指明月份，就默认显示当前月份的考勤数据
        if (month == null) {
            month = DateUtil.format(new java.util.Date(), "yyyyMM");
        }

        LocalDate baseDate = LocalDate.parse(month + "01", DateTimeFormatter.ofPattern("yyyyMMdd"));
        LocalDate startDate = baseDate.minusMonths(1).withDayOfMonth(23);
        LocalDate endDate = baseDate.withDayOfMonth(22);

        List<String> dateList = new ArrayList<>();
        LocalDate currentDate = startDate;
        while (!currentDate.isAfter(endDate)) {
            dateList.add(currentDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")));
            currentDate = currentDate.plusDays(1);
        }

        String[] monthDayList = dateList.toArray(new String[0]);


        for (StaffAttendanceVO staffDeptVO : staffDeptVOList) {
            List<HashMap<String, Object>> list = new ArrayList<>();
            for (String day : monthDayList) {
                HashMap<String, Object> map = new HashMap<>();
                Attendance attendance = this.attendanceMapper.queryByStaffIdAndDate(staffDeptVO.getStaffId(), day);

                if (attendance == null) {
                    Date date = DateUtil.parse(day, "yyyyMMdd").toSqlDate();
                    // 处理不存在考勤数据的情况
                    if (DateUtil.isWeekend(date) || this.datetimeUtil.isHoliday(date)) {
                        if (this.datetimeUtil.isHoliday(date)) {
                            // 法定节假日
                            map.put("code", AttendanceStatusEnum.PUBLIC_HOLIDAY.getCode()); // 新增code
                            map.put("message", AttendanceStatusEnum.PUBLIC_HOLIDAY.getMessage());
                            map.put("tagType", AttendanceStatusEnum.PUBLIC_HOLIDAY.getTagType());
                        } else if (DateUtil.isWeekend(date)) {
                            // 周末公休
                            map.put("code", AttendanceStatusEnum.LEAVE.getCode()); // 新增code
                            map.put("message", AttendanceStatusEnum.LEAVE.getMessage());
                            map.put("tagType", AttendanceStatusEnum.LEAVE.getTagType());
                        } else {
                            // 正常工作日（理论上不会进入这个分支）
                            map.put("code", AttendanceStatusEnum.NORMAL.getCode()); // 新增code
                            map.put("message", AttendanceStatusEnum.NORMAL.getMessage());
                            map.put("tagType", AttendanceStatusEnum.NORMAL.getTagType());
                        }
                    } else {
                        // 正常工作日
                        map.put("code", AttendanceStatusEnum.NORMAL.getCode()); // 新增code
                        map.put("message", AttendanceStatusEnum.NORMAL.getMessage());
                        map.put("tagType", AttendanceStatusEnum.NORMAL.getTagType());
                    }
                    map.put("attendanceDate", DateUtil.format(date, "yyyyMMdd"));
                } else {
                    // 存在考勤数据
                    map.put("code", attendance.getStatus().getCode()); // 新增code
                    map.put("message", attendance.getStatus().getMessage());
                    map.put("tagType", attendance.getStatus().getTagType());
                    map.put("attendanceDate", DateUtil.format(attendance.getAttendanceDate(), "yyyyMMdd"));
                }
                list.add(map);
            }
            staffDeptVO.setAttendanceList(list);
        }
        // 将响应数据填充到map中
        Map map = new HashMap();
        map.put("pages", page.getPages());
        map.put("total", page.getTotal());
        map.put("list", staffDeptVOList);
        map.put("dayNum", monthDayList.length);
        map.put("month", month);

        map.put("dayList", monthDayList); // 添加这行
        return Response.success(map);
    }




    /**
     * 导出员工月考勤数据
     *
     * @param response
     * @param
     * @param filename
     * @return
     */
    //--------------------------------------------------------------------------------------

    public void export(HttpServletResponse response, String monthParam, String filename) throws IOException {
        ResponseDTO responseDTO = list(1, Integer.MAX_VALUE, null, null, monthParam);
        Map<String, Object> dataMap = (Map<String, Object>) responseDTO.getData();

        List<Map<String, Object>> exportList = new ArrayList<>();
        List<StaffAttendanceVO> staffList = (List<StaffAttendanceVO>) dataMap.get("list");
        String[] monthDayList = (String[]) dataMap.get("dayList"); // 直接转为String数组
        int dayNum = monthDayList.length;

        // 调整后的列头顺序
        List<String> headers = new ArrayList<>(Arrays.asList("工号", "编号", "姓名", "部门"));
        // 动态日期列
        for (String day : monthDayList) {
            Date date = DateUtil.parse(day, "yyyyMMdd").toSqlDate();
            headers.add(DateUtil.format(date, "M-d")); // 格式示例：3-22
        }
        // 新增统计列（按新顺序）
        headers.addAll(Arrays.asList(
                "应出勤天数", "实际出勤天数", "培训工数", "轮休", "公休", "调休", "节假日",
                "井下工数", "大夜班", "小夜班", "特殊工种出勤/外勤", "旷工", "事假天数",
                "病假天数", "生育假天数", "婚假天数", "探亲假天数", "陪待假天数", "年休假天数",
                "工伤天数", "丧假天数", "流产假天数", "孕期检查假天数", "护理育儿假天数",
                "节育绝育假天数", "哺乳假天数"
        ));

        for (StaffAttendanceVO staff : staffList) {
            Map<String, Object> row = new LinkedHashMap<>();
            // 基础信息
            row.put("工号", staff.getCode());
            row.put("编号", staff.getNumber());
            row.put("姓名", staff.getName());
            row.put("部门", staff.getDeptName());

            // 初始化统计计数器
            Map<String, Integer> stats = new HashMap(){{
                put("NORMAL",0);       // 新增
                put("ON_SURFACE",0);   // 新增
                put("UNDERGROUND",0);  // 新增
                put("PUBLIC_HOLIDAY", 0); // 节假日
                put("LEAVE", 0);         // 公休
                put("TIME_OFF", 0);      // 调休
                put("STAGGER_HOLIDAY", 0); // 轮休
                put("UNDERGROUND_BIG_NIGHT", 0); // 井下大夜班
                put("UNDERGROUND_SMALL_NIGHT", 0);// 井下小夜班
                put("UNDERGROUND_DAY", 0);       // 井下大白班
                put("UNDERGROUND_LITTLE_DAY", 0); // 井下小白班
                put("ON_SURFACE_BIG_NIGHT_SHIFT", 0);       // 井上大夜班
                put("ON_SURFACE_SMALL_NIGHT_SHIFT", 0);     // 井上小夜班
                put("ON_SURFACE_BIG_DAY_SHIFT", 0);    // 井上大白班
                put("ON_SURFACE_LITTLE_WHITE_SHIFT",0);// 井上小白班
                put("FIELD_PERSONNEL", 0);       // 特殊工种
                put("TRAINING", 0);              // 培训
                put("ABSENTEEISM", 0);           // 旷工
                // 请假类型
                put("PERSONAL_LEAVE", 0);
                put("SICK_LEAVE", 0);
                put("MATERNITY_LEAVE",0);
                put("MARRIAGE_LEAVE",0);
                put("HOME_LEAVE",0);
                put("PATERNITY_LEAVE",0);
                put("ANNUAL_LEAVE",0);
                put("WORKIN_JURY_LEAVE",0);
                put("BEREAVEMENT_LEAVE",0);
                put("ABORTION_LEAVE",0);
                put("PREGNANCY_CHECK_UP_LEAVE",0);
                put("NURSING_LEAVE",0);
                put("BIRTH_CONTROL",0);
                put("BREASTFEEDING",0);
            }};

            // 统计考勤状态
            for (HashMap<String, Object> dayData : staff.getAttendanceList()) {
                String status = (String) dayData.get("message");
                switch (status) {
                    // 基础状态
                    case "法定节假日": stats.put("PUBLIC_HOLIDAY", stats.get("PUBLIC_HOLIDAY")+1); break;
                    case "公休": stats.put("LEAVE", stats.get("LEAVE")+1); break;
                    case "调休": stats.put("TIME_OFF", stats.get("TIME_OFF")+1); break;
                    case "培训": stats.put("TRAINING", stats.get("TRAINING")+1); break;
                    case "特殊工种出勤/外勤": stats.put("FIELD_PERSONNEL", stats.get("FIELD_PERSONNEL")+1); break;
                    case "旷工": stats.put("ABSENTEEISM", stats.get("ABSENTEEISM")+1); break;
                    case "轮休": stats.put("STAGGER_HOLIDAY", stats.get("STAGGER_HOLIDAY")+1);break;

                    case "正常": stats.put("NORMAL", stats.get("NORMAL")+1);break;
                    case "井上": stats.put("ON_SURFACE", stats.get("ON_SURFACE")+1);break;
                    case "井下": stats.put("UNDERGROUND", stats.get("UNDERGROUND")+1);break;

                    // 井下相关
                    case "井下大夜班": stats.put("UNDERGROUND_BIG_NIGHT", stats.get("UNDERGROUND_BIG_NIGHT")+1); break;
                    case "井下小夜班": stats.put("UNDERGROUND_SMALL_NIGHT", stats.get("UNDERGROUND_SMALL_NIGHT")+1); break;
                    case "井下大白班": stats.put("UNDERGROUND_DAY", stats.get("UNDERGROUND_DAY")+1); break;
                    case "井下小白班": stats.put("UNDERGROUND_LITTLE_DAY", stats.get("UNDERGROUND_LITTLE_DAY")+1);break;
                    // 井上相关
                    case "井上大夜班": stats.put("ON_SURFACE_BIG_NIGHT_SHIFT", stats.get("ON_SURFACE_BIG_NIGHT_SHIFT")+1); break;
                    case "井上小夜班": stats.put("ON_SURFACE_SMALL_NIGHT_SHIFT", stats.get("ON_SURFACE_SMALL_NIGHT_SHIFT")+1); break;
                    case "井上大白班": stats.put("ON_SURFACE_BIG_DAY_SHIFT", stats.get("ON_SURFACE_BIG_DAY_SHIFT")+1);break;
                    case "井上小白班": stats.put("ON_SURFACE_LITTLE_WHITE_SHIFT", stats.get("ON_SURFACE_LITTLE_WHITE_SHIFT")+1);break;
                    // 请假类型
                    case "事假": stats.put("PERSONAL_LEAVE", stats.get("PERSONAL_LEAVE")+1); break;
                    case "病假": stats.put("SICK_LEAVE", stats.get("SICK_LEAVE")+1); break;
                    case "生育假": stats.put("MATERNITY_LEAVE", stats.get("MATERNITY_LEAVE")+1); break;
                    case "婚假": stats.put("MARRIAGE_LEAVE", stats.get("MARRIAGE_LEAVE")+1); break;
                    case "探亲假": stats.put("HOME_LEAVE", stats.get("HOME_LEAVE")+1); break;
                    case "陪待假": stats.put("PATERNITY_LEAVE", stats.get("PATERNITY_LEAVE")+1); break;
                    case "年休假": stats.put("ANNUAL_LEAVE", stats.get("ANNUAL_LEAVE")+1); break;
                    case "工伤": stats.put("WORKIN_JURY_LEAVE", stats.get("WORKIN_JURY_LEAVE")+1); break;
                    case "丧假": stats.put("BEREAVEMENT_LEAVE", stats.get("BEREAVEMENT_LEAVE")+1); break;
                    case "流产假": stats.put("ABORTION_LEAVE", stats.get("ABORTION_LEAVE")+1); break;
                    case "孕期检查假": stats.put("PREGNANCY_CHECK_UP_LEAVE", stats.get("PREGNANCY_CHECK_UP_LEAVE")+1); break;
                    case "护理育儿假": stats.put("NURSING_LEAVE", stats.get("NURSING_LEAVE")+1); break;
                    case "节育绝育假": stats.put("BIRTH_CONTROL", stats.get("BIRTH_CONTROL")+1); break;
                    case "哺乳假": stats.put("BREASTFEEDING", stats.get("BREASTFEEDING")+1); break;
                }
            }

            // 计算组合字段
            int undergroundTotal =
                    stats.getOrDefault("UNDERGROUND_BIG_NIGHT",0)
                            + stats.getOrDefault("UNDERGROUND_SMALL_NIGHT",0)
                            + stats.getOrDefault("UNDERGROUND_DAY",0)
                            + stats.getOrDefault("UNDERGROUND_LITTLE_DAY",0);

            int bigNightTotal = stats.getOrDefault("UNDERGROUND_BIG_NIGHT",0)
                    + stats.getOrDefault("ON_SURFACE_BIG_NIGHT_SHIFT",0);


            int smallNightTotal = stats.getOrDefault("UNDERGROUND_SMALL_NIGHT",0)
                    + stats.getOrDefault("ON_SURFACE_SMALL_NIGHT_SHIFT",0);



            // 应出勤天数 = 总天数 - 公休 - 法定节假日
            int shouldAttendDays = staff.getAttendanceList().size()
                    - stats.get("LEAVE")
                    - stats.get("PUBLIC_HOLIDAY");

            // 统计所有正常出勤类型（井上/井下各类班次）
            int normalAttendance = stats.getOrDefault("NORMAL",0) // 包含正常状态
                    + stats.getOrDefault("ON_SURFACE",0) // 井上
                    + stats.getOrDefault("UNDERGROUND",0) // 井下
                    + stats.getOrDefault("ON_SURFACE_BIG_NIGHT_SHIFT",0)
                    + stats.getOrDefault("ON_SURFACE_SMALL_NIGHT_SHIFT",0)
                    + stats.getOrDefault("ON_SURFACE_BIG_DAY_SHIFT",0)
                    + stats.getOrDefault("ON_SURFACE_LITTLE_WHITE_SHIFT",0)
                    + stats.getOrDefault("UNDERGROUND_BIG_NIGHT",0)
                    + stats.getOrDefault("UNDERGROUND_SMALL_NIGHT",0)
                    + stats.getOrDefault("UNDERGROUND_DAY",0)
                    + stats.getOrDefault("UNDERGROUND_LITTLE_DAY",0);



            // 实际出勤天数 = 所有正常出勤类型总和
            int actualAttendDays = normalAttendance;

            // 确保不小于0
            actualAttendDays = Math.max(actualAttendDays, 0);

            // 填充统计列（按新顺序）
            row.put("应出勤天数", shouldAttendDays);
            row.put("实际出勤天数", Math.max(actualAttendDays, 0));
            row.put("培训工数", stats.get("TRAINING"));
            row.put("轮休", 0); // 需要根据业务补充逻辑
            row.put("公休", stats.get("LEAVE"));
            row.put("调休", stats.get("TIME_OFF"));
            row.put("节假日", stats.get("PUBLIC_HOLIDAY"));
            row.put("井下工数", undergroundTotal);
            row.put("大夜班", bigNightTotal);
            row.put("小夜班", smallNightTotal);
            row.put("特殊工种出勤/外勤", stats.get("FIELD_PERSONNEL"));
            row.put("旷工", stats.get("ABSENTEEISM"));
            // 请假类型
            row.put("事假天数", stats.get("PERSONAL_LEAVE"));
            row.put("病假天数", stats.get("SICK_LEAVE"));
            row.put("生育假天数", stats.get("MATERNITY_LEAVE"));
            row.put("婚假天数", stats.get("MARRIAGE_LEAVE"));
            row.put("探亲假天数", stats.get("HOME_LEAVE"));
            row.put("陪待假天数", stats.get("PATERNITY_LEAVE"));
            row.put("年休假天数", stats.get("ANNUAL_LEAVE"));
            row.put("工伤天数", stats.get("WORKIN_JURY_LEAVE"));
            row.put("丧假天数", stats.get("BEREAVEMENT_LEAVE"));
            row.put("流产假天数", stats.get("ABORTION_LEAVE"));
            row.put("孕期检查假天数", stats.get("PREGNANCY_CHECK_UP_LEAVE"));
            row.put("护理育儿假天数", stats.get("NURSING_LEAVE"));
            row.put("节育绝育假天数", stats.get("BIRTH_CONTROL"));
            row.put("哺乳假天数", stats.get("BREASTFEEDING"));

            // 日期列数据
            for (int i = 0; i < dayNum; i++) {
                HashMap<String, Object> dayData = staff.getAttendanceList().get(i);
                String dateStr = (String) dayData.get("attendanceDate");
                Date date = DateUtil.parse(dateStr, "yyyyMMdd").toSqlDate();
                row.put(DateUtil.format(date, "M-d"), dayData.get("message"));
            }
            exportList.add(row);
        }

        // Excel生成逻辑（带样式）
        ExcelWriter writer = ExcelUtil.getWriter(true);
        try {
            // ==== 修正后的标题生成逻辑 ====
            // 解析月份参数（格式：yyyyMM）
            LocalDate baseDate = LocalDate.parse(monthParam + "01", DateTimeFormatter.ofPattern("yyyyMMdd"));

            // 计算起始日（上月23日）
            LocalDate startDate = baseDate.minusMonths(1).withDayOfMonth(23);

            // 计算结束日（本月22日）
            LocalDate endDate = baseDate.withDayOfMonth(22);

            // 生成标题
            String title = String.format("员工考勤信息表(%s到%s)",
                    startDate.format(DateTimeFormatter.ofPattern("MM月dd日")),
                    endDate.format(DateTimeFormatter.ofPattern("MM月dd日")));

            // 合并首行单元格（合并所有列）
            writer.merge(headers.size() - 1, title);

            // 设置标题样式
            Cell titleCell = writer.getCell(0, 0);
            Font titleFont = writer.createFont();
            titleFont.setBold(true);
            titleFont.setFontHeightInPoints((short) 16);

            CellStyle titleStyle = writer.createCellStyle();
            titleStyle.setFont(titleFont);
            titleStyle.setAlignment(HorizontalAlignment.CENTER);
            titleCell.setCellStyle(titleStyle);
            writer.setRowHeight(0, 30); // 标题行高度

            // ==== 表头设置 ====
            writer.setCurrentRow(1); // 表头从第二行开始

            // ==== 样式设置 ====
            // 全局边框样式
            CellStyle borderStyle = writer.getWorkbook().createCellStyle();
            borderStyle.setBorderTop(BorderStyle.THIN);
            borderStyle.setBorderBottom(BorderStyle.THIN);
            borderStyle.setBorderLeft(BorderStyle.THIN);
            borderStyle.setBorderRight(BorderStyle.THIN);

            // 状态颜色样式
            Map<String, CellStyle> styleMap = new HashMap<>();
            Workbook workbook = writer.getWorkbook();

            // 颜色配置（新增请假类型颜色）
            Map<String, IndexedColors> colorConfig = new HashMap<String, IndexedColors>() {{
                put("正常", IndexedColors.DARK_GREEN);
                put("迟到", IndexedColors.RED);
                put("早退", IndexedColors.ORANGE);
                put("旷工", IndexedColors.RED);
                put("调休", IndexedColors.BLUE);
                put("培训", IndexedColors.TEAL);
                put("井下大夜班", IndexedColors.DARK_BLUE);
                put("井下小夜班", IndexedColors.ROYAL_BLUE);
                put("特殊工种出勤/外勤", IndexedColors.GREY_50_PERCENT);
                put("事假", IndexedColors.ROSE);          // 新增
                put("病假", IndexedColors.LIGHT_BLUE);    // 新增
                put("婚假", IndexedColors.PINK);          // 新增
            }};

            // 创建带边框的样式
            colorConfig.forEach((status, color) -> {
                Font font = workbook.createFont();
                font.setColor(color.getIndex());

                CellStyle style = workbook.createCellStyle();
                style.setFont(font);
                style.setBorderTop(BorderStyle.THIN);
                style.setBorderBottom(BorderStyle.THIN);
                style.setBorderLeft(BorderStyle.THIN);
                style.setBorderRight(BorderStyle.THIN);
                styleMap.put(status, style);
            });

            // 默认样式（黑色字体+边框）
            CellStyle defaultStyle = workbook.createCellStyle();
            Font defaultFont = workbook.createFont();
            defaultFont.setColor(IndexedColors.BLACK.getIndex());
            defaultStyle.setFont(defaultFont);
            defaultStyle.setBorderTop(BorderStyle.THIN);
            defaultStyle.setBorderBottom(BorderStyle.THIN);
            defaultStyle.setBorderLeft(BorderStyle.THIN);
            defaultStyle.setBorderRight(BorderStyle.THIN);
            styleMap.put("默认", defaultStyle);

            // ==== 数据写入 ====
            // 写入列头（从第二行开始）
            writer.writeHeadRow(headers);

            // 设置基础列宽
            int[] baseWidths = {12, 10, 10, 15}; // 工号、编号、姓名、部门
            for (int i = 0; i < baseWidths.length; i++) {
                writer.setColumnWidth(i, Math.min(baseWidths[i], 255));
            }

            // 写入数据行
            for (Map<String, Object> row : exportList) {
                List<Object> rowData = new ArrayList<>();
                List<CellStyle> styles = new ArrayList<>();

                // 基础列（工号、编号、姓名、部门）
                for (int i = 0; i < 4; i++) {
                    rowData.add(row.get(headers.get(i)));
                    styles.add(borderStyle);
                }

                // 日期列（带状态颜色）
                for (int i = 4; i < headers.indexOf("应出勤天数"); i++) {
                    String status = (String) row.get(headers.get(i));
                    rowData.add(status);
                    styles.add(styleMap.getOrDefault(status, styleMap.get("默认")));
                }

                // 统计列（统一边框）
                for (int i = headers.indexOf("应出勤天数"); i < headers.size(); i++) {
                    rowData.add(row.get(headers.get(i)));
                    styles.add(borderStyle);
                }

                writer.writeRow(rowData, false);
                setRowStyles(writer, writer.getCurrentRow() - 1, styles);
            }

            // ==== 列宽调整 ====
            // 日期列统一宽度
            IntStream.range(4, headers.indexOf("应出勤天数")).forEach(i ->
                    writer.setColumnWidth(i, Math.min(12, 255)));

            // 统计列统一16字符宽
            IntStream.range(headers.indexOf("应出勤天数"), headers.size()).forEach(i ->
                    writer.setColumnWidth(i, Math.min(16, 255)));

            // ==== 响应输出 ====
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-Disposition",
                    "attachment;filename=" + URLEncoder.encode(filename, "UTF-8") + ".xlsx");
            writer.flush(response.getOutputStream(), true);
        } finally {
            writer.close();
        }
    }

    // 辅助方法：设置行样式
    private void setRowStyles(ExcelWriter writer, int rowNum, List<CellStyle> styles) {
        Row row = writer.getSheet().getRow(rowNum);
        for (int i = 0; i < styles.size(); i++) {
            if (styles.get(i) != null && row.getCell(i) != null) {
                row.getCell(i).setCellStyle(styles.get(i));
            }
        }
    }

    //-----------------------------------------------------------------------------










    /**
     * 数据导入（修改）
     *
     * @param file
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public ResponseDTO imp(MultipartFile file) throws IOException {
        InputStream inputStream = file.getInputStream();
        List<Attendance> list = HutoolExcelUtil.readExcel(inputStream, 1, Attendance.class);
        for (Attendance attendance : list) {
            // 判断是否是周末，如果是周末就不用记录考勤情况，如果不是周末就判断员工是否请假
            if (attendance.getStaffId() == null || attendance.getAttendanceDate() == null ||
                    DateUtil.isWeekend(attendance.getAttendanceDate()) || isLeave(attendance)) {
                continue;
            }


            Dept dept = this.deptMapper.queryByStaffId(attendance.getStaffId());
            if (dept == null) {
                return Response.error(BusinessStatusEnum.DEPT_NOT_FOUND);
            }
            if (StringUtils.isBlank(dept.getCode())) {
                return Response.error(BusinessStatusEnum.DEPT_CODE_MISSING);
            }


            if (isAbsenteeism(attendance, dept)) {
                attendance.setStatus(AttendanceStatusEnum.ABSENTEEISM);
            } else if (isLate(attendance, dept)) {
                attendance.setStatus(AttendanceStatusEnum.LATE);
            } else if (isLeaveEarly(attendance, dept)) {
                attendance.setStatus(AttendanceStatusEnum.LEAVE_EARLY);
            } else {
                attendance.setStatus(AttendanceStatusEnum.NORMAL);
            }
            QueryWrapper<Attendance> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("staff_id", attendance.getStaffId()).eq("attendance_date",
                    attendance.getAttendanceDate());
            if (!saveOrUpdate(attendance, queryWrapper)) {
                return Response.error(BusinessStatusEnum.DATA_IMPORT_ERROR);
            }
        }
        return Response.success();
    }







    public ResponseDTO setAttendance(Attendance attendance) {
        QueryWrapper<Attendance> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("staff_id", attendance.getStaffId())
                .eq("attendance_date", attendance.getAttendanceDate());

        //禁止将请假类型状态修改为非请假状态
        Attendance original = getOne(queryWrapper);
        if (original != null) {
            int originalCode = original.getStatus().getCode();
            int newCode = attendance.getStatus().getCode();

            // 请假类型状态码范围：20-33
            boolean isOriginalLeave = originalCode >= 20 && originalCode <= 33;
            boolean isNewLeave = newCode >= 20 && newCode <= 33;

            if (isOriginalLeave && !isNewLeave) {
                return Response.error("禁止将请假类型状态修改为非请假状态");
            }
        }


        if (saveOrUpdate(attendance, queryWrapper)) {
            Attendance updated = getOne(queryWrapper);
            // ▼▼▼ 强制加载状态枚举的显示属性 ▼▼▼
            HashMap<String, Object> result = new HashMap<>();
            result.put("staffId", updated.getStaffId());
            result.put("attendanceDate", DateUtil.format(updated.getAttendanceDate(), "yyyyMMdd"));
            result.put("status", new HashMap<>(){{
                put("code", updated.getStatus().getCode());
                put("message", updated.getStatus().getMessage());
                put("tagType", updated.getStatus().getTagType());
            }});
            return Response.success(result);
        }
        return Response.error();
    }







    /**
     * 是否迟到
     *
     * @param attendance
     * @param dept
     * @return
     */
    private boolean isLate(Attendance attendance, Dept dept) {
        if (DateUtil.compare(attendance.getMorStartTime(), dept.getMorStartTime(), "HH:mm:ss") > 0) {
            return true;
        } else if (DateUtil.compare(attendance.getAftStartTime(), dept.getAftStartTime(), "HH:mm:ss") > 0) {
            return true;
        }
        return false;
    }

    /**
     * 是否早退
     *
     * @param attendance
     * @param dept
     * @return
     */
    private boolean isLeaveEarly(Attendance attendance, Dept dept) {
        if (DateUtil.compare(attendance.getMorEndTime(), dept.getMorEndTime(), "HH:mm:ss") < 0) {
            return true;
        } else if (DateUtil.compare(attendance.getAftEndTime(), dept.getAftEndTime(), "HH:mm:ss") < 0) {
            return true;
        }
        return false;
    }

    /**
     * 是否旷工，如果4个打卡，有一个没打卡，就视为旷工，如果既迟到又早退，也使视为旷工
     *
     * @param attendance
     * @param dept
     * @return
     */
    private boolean isAbsenteeism(Attendance attendance, Dept dept) {
        if (attendance.getMorStartTime() == null || attendance.getMorEndTime() == null
                || attendance.getAftStartTime() == null || attendance.getAftEndTime() == null) {
            return true;
        }
        return isLate(attendance, dept) && isLeaveEarly(attendance, dept);
    }

    /**
     * 判断员工是否请假或调休
     *
     * @param attendance
     * @return
     */
    private boolean isLeave(Attendance attendance) {
        QueryWrapper<Attendance> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("staff_id", attendance.getStaffId()).eq("attendance_date", attendance.getAttendanceDate());
        Attendance one = getOne(queryWrapper);
        if (one != null) {
            return one.getStatus() == AttendanceStatusEnum.LEAVE || one.getStatus() == AttendanceStatusEnum.TIME_OFF;
        }
        return false;
    }
















    public ResponseDTO queryAll() {
        List<AttendanceStatusEnum> allStatus = Arrays.stream(AttendanceStatusEnum.values())
                .filter(status -> !status.isAuto()) // 错误过滤了部分节点
                .collect(Collectors.toList());

        Map<Integer, List<AttendanceStatusEnum>> parentMap = allStatus.stream()
                .collect(Collectors.groupingBy(
                        status -> status.getParentCode() != null ? status.getParentCode() : -1
                ));

        // 3. 构建顶级节点（parentCode=null的节点）
        List<Map<String, Object>> topLevel = allStatus.stream()
                .filter(status -> status.getParentCode() == null)
                .map(status -> buildTree(status, parentMap))
                .collect(Collectors.toList());

        return Response.success(topLevel);
    }

    // 递归构建树节点
    private Map<String, Object> buildTree(AttendanceStatusEnum status, Map<Integer, List<AttendanceStatusEnum>> parentMap) {
        Map<String, Object> node = new HashMap<>();
        node.put("code", status.getCode());
        node.put("message", status.getMessage());
        node.put("tagType", status.getTagType());

        // 获取子节点（处理可能的null值）
        List<AttendanceStatusEnum> children = parentMap.getOrDefault(status.getCode(), Collections.emptyList());

        // 递归构建子树
        if (!children.isEmpty()) {
            List<Map<String, Object>> childNodes = children.stream()
                    .map(child -> buildTree(child, parentMap))
                    .collect(Collectors.toList());
            node.put("children", childNodes);
        }
        return node;
    }















































    public ResponseDTO queryByStaffIdAndDate(Integer id, String date) {
        String cleanDate = date.replaceAll("-", "");
        Attendance attendance = this.attendanceMapper.queryByStaffIdAndDate(id, cleanDate);
        if (attendance != null) {
            return Response.success(attendance);
        }
        return Response.error();
    }
}
